// 版权所有2009年围棋作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package pkginit

import (
	"cmd/compile/internal/base"
	"cmd/compile/internal/deadcode"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/objw"
	"cmd/compile/internal/typecheck"
	"cmd/compile/internal/types"
	"cmd/internal/obj"
)

// 任务生成并返回包的初始化记录。
// 有关其布局，请参见runtime/proc.go:initTask。
// 初始化的3个任务是：
// 1） 初始化当前包所依赖的所有包。
// 2） 初始化所有具有初始值设定项的变量。
// 3） 运行任何init函数。
func Task() *ir.Name {
	nf := initOrder(typecheck.Target.Decls)

	var deps []*obj.LSym // 当前包所依赖的包的initTask记录
	var fns []*obj.LSym  // 调用包初始化的函数

	// 查找包含init任务的导入包。
	for _, pkg := range typecheck.Target.Imports {
		n := typecheck.Resolve(ir.NewIdent(base.Pos, pkg.Lookup(".inittask")))
		if n.Op() == ir.ONONAME {
			continue
		}
		if n.Op() != ir.ONAME || n.(*ir.Name).Class != ir.PEXTERN {
			base.Fatalf("bad inittask: %v", n)
		}
		deps = append(deps, n.(*ir.Name).Linksym())
	}

	// 创建一个包含所有初始化语句的函数。
	if len(nf) > 0 {
		base.Pos = nf[0].Pos() // prolog/epilog获取第一个init stmt的行号
		initializers := typecheck.Lookup("init")
		fn := typecheck.DeclFunc(initializers, ir.NewFuncType(base.Pos, nil, nil, nil))
		for _, dcl := range typecheck.InitTodoFunc.Dcl {
			dcl.Curfn = fn
		}
		fn.Dcl = append(fn.Dcl, typecheck.InitTodoFunc.Dcl...)
		typecheck.InitTodoFunc.Dcl = nil

		fn.Body = nf
		typecheck.FinishFuncBody()

		typecheck.Func(fn)
		ir.CurFunc = fn
		typecheck.Stmts(nf)
		ir.CurFunc = nil
		typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
		fns = append(fns, fn.Linksym())
	}
	if typecheck.InitTodoFunc.Dcl != nil {
		// 如果有，我们只使用InitTodoFunc生成temp
		// 是包范围初始化语句，所以
		// 如果我们到了这里，会有些奇怪。
		base.Fatalf("InitTodoFunc still has declarations")
	}
	typecheck.InitTodoFunc = nil

	// 记录用户初始化函数。
	for _, fn := range typecheck.Target.Inits {
		// 必须在订单之后发生；见#43444。
		deadcode.Func(fn)

		// 跳过具有空实体的初始化函数。
		if len(fn.Body) == 1 {
			if stmt := fn.Body[0]; stmt.Op() == ir.OBLOCK && len(stmt.(*ir.BlockStmt).List) == 0 {
				continue
			}
		}
		fns = append(fns, fn.Nname.Linksym())
	}

	if len(deps) == 0 && len(fns) == 0 && types.LocalPkg.Name != "main" && types.LocalPkg.Name != "runtime" {
		return nil // 没有要初始化的内容
	}

	// 创建一个.inittask结构。
	sym := typecheck.Lookup(".inittask")
	task := typecheck.NewName(sym)
	task.SetType(types.Types[types.TUINT8]) // 假类型
	task.Class = ir.PEXTERN
	sym.Def = task
	lsym := task.Linksym()
	ot := 0
	ot = objw.Uintptr(lsym, ot, 0) // 状态：尚未初始化
	ot = objw.Uintptr(lsym, ot, uint64(len(deps)))
	ot = objw.Uintptr(lsym, ot, uint64(len(fns)))
	for _, d := range deps {
		ot = objw.SymPtr(lsym, ot, d, 0)
	}
	for _, f := range fns {
		ot = objw.SymPtr(lsym, ot, f, 0)
	}
	// initTask有指针，但没有指针进入Go堆。
	// 它不是完全只读的，状态字段必须是可修改的。
	objw.Global(lsym, int32(ot), obj.NOPTR)
	return task
}
